<template>
  <div
    id="container"
    style="width: 100%; height: 100vh; position: relative; overflow: hidden;"
  >
  <div id="tooltip"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import chinaJson from '../../../../public/static/geoJson/china.json'
import chinaBorder from '../../../../public/static/geoJson/chinaBorder.json'
import {
    OrbitControls
} from 'three/examples/jsm/controls/OrbitControls.js'
// import { OrbitControls } from './jsm/controls/OrbitControls.js';
let width = null;
let height = null;
const globeTextureLoader = new THREE.TextureLoader();
// 地球  group
const group = new THREE.Group();
// 卫星平面 group
const groupHalo = new THREE.Group();
// 地球半径
const radius = 50
export default {
  name: "threeDemo1",
  data() {
    return {
      Dom: null,
      renderer: null,
      camera: null,
      scene: null,
      light: null,
      controls: null,
      raycaster: null,
      mouse: null,
      lastPick: null,
      tooltip: null,
      waveMash: null,
      commonUniforms: {
            time: {
                value: 0
            },
            number: {
                value: 1
            },
            speed: {
                value: 0.5
            },
            length: {
                value: 0.4
            },
            size: {
                value: 4
            }
        }
    };
  },
  methods: {
    /**
     * @description 初始化渲染场景
     */
    initRenderer() {
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setSize(width, height);
      const containerDom = document.querySelector("#container");
      containerDom.appendChild(this.renderer.domElement);
    },
    /**
     * @description 初始化相机
     */
    initCamera() {
      this.camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
      this.camera.position.set(5, -20, 200);
      this.camera.lookAt(0, 3, 0);
    },
    /**
     * @description 初始化场景
     */
    initScene() {
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0x020924);
      this.scene.fog = new THREE.Fog(0x020924, 200, 1000);
    },
    /**
     * 初始化用户交互
     **/
    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.enableZoom = true;
      this.controls.autoRotate = false;
      this.controls.autoRotateSpeed = 2;
      this.controls.enablePan = true;
    },
    /**
     * @description 初始化光
     */
    initLight() {
      const ambientLight = new THREE.AmbientLight(0xcccccc, 1.1);
      this.scene.add(ambientLight);
      var directionalLight = new THREE.DirectionalLight(0xffffff, 0.2);
      directionalLight.position.set(1, 0.1, 0).normalize();
      var directionalLight2 = new THREE.DirectionalLight(0xff2ffff, 0.2);
      directionalLight2.position.set(1, 0.1, 0.1).normalize();
      this.scene.add(directionalLight);
      this.scene.add(directionalLight2);
      var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.2);
      hemiLight.position.set(0, 1, 0);
      this.scene.add(hemiLight);
      directionalLight = new THREE.DirectionalLight(0xffffff);
      directionalLight.position.set(1, 500, -20);
      directionalLight.castShadow = true;
      directionalLight.shadow.camera.top = 18;
      directionalLight.shadow.camera.bottom = -10;
      directionalLight.shadow.camera.left = -52;
      directionalLight.shadow.camera.right = 12;
      this.scene.add(directionalLight);
    },
    /**
     * 窗口变动
     **/
    onWindowResize() {
      this.camera.aspect = innerWidth / innerHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(innerWidth, innerHeight);
      console.log("gg");
      this.renders();
    },
    /**
     * @description 渲染
     */
    renders() {
      this.renderer.clear();
      this.renderer.render(this.scene, this.camera);
    },
    /**
     * 更新
     **/
    animate() {
      window.requestAnimationFrame(() => {
        if (this.controls) this.controls.update();
        // 通过摄像机和鼠标位置更新射线
        this.raycaster.setFromCamera(this.mouse, this.camera)
        // 算出射线 与当场景相交的对象有那些
        const intersects = this.raycaster.intersectObjects(
          this.scene.children,
          true
        )
        // 显示tooltip
        this.showTip()
        // 恢复上一次清空的
        if (this.lastPick) {
          this.lastPick.object.material.color.set(0XF19553)
        }
        this.lastPick = null
        this.lastPick = intersects.find(
           (item) => item.object.type === 'Line'
        )
        if (this.lastPick) {
          this.lastPick.object.material.color.set(0xff0000)
        }
        // mark点水波纹动画
        this.markWaveAnmial(this.waveMash)
        // 着色器 uniform变量修改
        this.commonUniforms.time.value += 0.01;
        this.renders();
        this.animate();
      });
    },
    // mark点水波纹
    markWaveAnmial(mesh) {
      // console.log(mesh, '看啊看吧')
      mesh._s += 0.07;
      mesh.size = radius * 0.04
        mesh.scale.set( mesh.size * mesh._s, mesh.size * mesh._s, mesh.size * mesh._s );
        if (mesh._s <= 1.5) {
           //mesh._s=1，透明度=0 mesh._s=1.5，透明度=1
          mesh.material.opacity = ( mesh._s - 1 ) * 2;
        } else if (mesh._s > 1.5 && mesh._s <= 2) {
           //mesh._s=1.5，透明度=1 mesh._s=2，透明度=0
           mesh.material.opacity = 1 - ( mesh._s - 1.5 ) * 2;
        } else {
           mesh._s = 1.0;
       }
    },

    //  基底之外业务---分割线
    // 随机生成1000个坐标点，设置不同的颜色
    setRandomDot() {
      const positions = [];
      const colors = [];
      const geometry = new THREE.BufferGeometry();
      for (var i = 0; i < 10000; i++) {
        var vertex = new THREE.Vector3();
        vertex.x = Math.random() * 2 - 1;
        vertex.y = Math.random() * 2 - 1;
        vertex.z = Math.random() * 2 - 1;
        positions.push(vertex.x, vertex.y, vertex.z);
        var color = new THREE.Color();
        color.setHSL(
          Math.random() * 0.2 + 0.5, 0.55, Math.random() * 0.25 + 0.55
        );
        colors.push(color.r, color.g, color.b);
      }
      geometry.setAttribute(
        "position",
        new THREE.Float32BufferAttribute(positions, 3)
      );
      geometry.setAttribute(
        "color",
        new THREE.Float32BufferAttribute(colors, 3)
      );
      return geometry;
    },
    // 创建基础粒子材质
    setPMat() {
      let texture = THREE.ImageUtils.loadTexture("static/textures/logo.png");
      const starsMaterial = new THREE.ParticleBasicMaterial({
        map: texture,
        size: 1,
        transparent: true,
        opacity: 1,
        vertexColors: true, //true：且该几何体的colors属性有值，则该粒子会舍弃第一个属性--color，而应用该几何体的colors属性的颜色
        blending: THREE.AdditiveBlending,
        sizeAttenuation: true
      });
      return starsMaterial;
    },
    // 几何体geometry和材质particleBasicMaterial结合 创建粒子系统
    setPSys() {
      let geometry = this.setRandomDot();
      let starsMaterial = this.setPMat();
      let stars = new THREE.ParticleSystem(geometry, starsMaterial);
      stars.scale.set(300, 300, 300);
      this.scene.add(stars);
    },
    initEarth() {
        globeTextureLoader.load( 'static/textures/earthSpec.png',  ( texture ) => {
            var globeGgeometry = new THREE.SphereGeometry( radius, 100, 100 );
            var globeMaterial = new THREE.MeshStandardMaterial( { map: texture } );
            var globeMesh = new THREE.Mesh( globeGgeometry, globeMaterial );
            // group.rotation.set( 0.5, 2.9, 0.1 );
            group.add( globeMesh );
            this.scene.add( group );
        } );
        },
    initEarthCircle() {
        let texture = globeTextureLoader.load( 'static/textures/light.png' );
        let spriteMaterial = new THREE.SpriteMaterial( {
          map: texture,
          transparent: true,
          opacity: 0.5,
          depthWrite: false
        } );
        let sprite = new THREE.Sprite( spriteMaterial );
        sprite.scale.set( radius * 2.52, radius * 2.52, 1 );
        group.add( sprite );
    },
    initWeiXingAndPlane() {
        // 添加平面
        globeTextureLoader.load( 'static/textures/redCircle2.png',  ( texture ) => {
          let geometry = new THREE.PlaneGeometry( 180, 180 );
          let material = new THREE.MeshLambertMaterial( {
            map: texture, 
            transparent: true,
            side: THREE.DoubleSide, 
            depthWrite: false
          } );
          let mesh = new THREE.Mesh( geometry, material );
          groupHalo.add( mesh );
          this.scene.add( groupHalo );
        } );
        // 添加两个点
         globeTextureLoader.load( 'static/textures/redCircle2.png', function ( texture ) {
          let p1 = new THREE.Vector3( - 70, 0, 0 );
          let p2 = new THREE.Vector3( 70, 0, 0 );
          const points = [ p1,p2];
          const geometry = new THREE.BufferGeometry().setFromPoints( points );
          let material = new THREE.PointsMaterial( {
            map: texture,
            transparent: true,
            side: THREE.DoubleSide, 
            size: 10, 
            depthWrite: false
          } );
          let earthPoints = new THREE.Points( geometry, material );
          groupHalo.add( earthPoints );
        } );
        groupHalo.rotation.set( 1.9, 0.5, 1 );

    },
    // three 自带经纬度转换
    /**
    *lng:经度
    *lat:维度
    *radius:地球半径
    */
    lglt2xyz(lng, lat, radius) {
      const theta = (90 + lng) * (Math.PI / 180)
      const phi = (90 - lat) * (Math.PI / 180)
      return (new THREE.Vector3()).setFromSpherical(new THREE.Spherical(radius, phi, theta))
    },
    // 标注及标注扩散光圈
    // 要实现标注功能很简单，直接一个平面加贴图即可
    // 这里唯一要注意的就是，再球面上的物体，需要设置好角度，不然你会发现效果会和你预想的不一样
    // 具体参考下面方法，
    createPointMesh( pos, texture, planeSize ) {
      let planGeometry = new THREE.PlaneGeometry( planeSize, planeSize );
      let material = new THREE.MeshBasicMaterial( {
        map: texture,
        transparent: true, //使用背景透明的png贴图，注意开启透明计算
        side: THREE.DoubleSide, //双面可见
        depthWrite: false, //禁止写入深度缓冲区数据
      } );
      var mesh = new THREE.Mesh( planGeometry, material );
      var size = radius * 0.04;//矩形平面Mesh的尺寸
      mesh.scale.set( size, size, size );//设置mesh大小
      //设置mesh位置
      mesh.position.set( pos.x, pos.y, pos.z );
      // mesh在球面上的法线方向(球心和球面坐标构成的方向向量)
      var coordVec3 = new THREE.Vector3( pos.x, pos.y, pos.z ).normalize();
      // mesh默认在XOY平面上，法线方向沿着z轴new THREE.Vector3(0, 0, 1)
      var meshNormal = new THREE.Vector3( 0, 0, 1 );
      // 四元数属性.quaternion表示mesh的角度状态
      //.setFromUnitVectors();计算两个向量之间构成的四元数值
      mesh.quaternion.setFromUnitVectors( meshNormal, coordVec3 );
      return mesh;
    },
    // 添加标注点
    addMarkDot() {
      let texture = globeTextureLoader.load( 'static/textures/dot.png' );
      let pos1 = this.lglt2xyz(116,39,radius)
      // 标记点 mark
      let pointMesh = this.createPointMesh(pos1, texture, 1)
      let pos3 = this.lglt2xyz(87,43,radius)
      let point3Mesh = this.createPointMesh(pos3, texture, 1)
      this.scene.add(pointMesh)
      this.scene.add(point3Mesh)
      // 标记点 mark周围波纹
      let texture2 = globeTextureLoader.load( 'static/textures/redCircle2.png' );
      let pos2 = this.lglt2xyz(106,29,radius)
      let pointWaveMesh = this.createPointMesh(pos2, texture2, 1.5)
      this.waveMash = pointWaveMesh
      this.scene.add( pointWaveMesh )
    },
    // 读取json数据生成地图
    initMap( chinaJson ) {
        // const map = new THREE.Group();
        const map = new THREE.Object3D()
        // 遍历省份构建模型
        chinaJson.features.forEach( elem => {
          // 新建一个省份容器：用来存放省份对应的模型和轮廓线
          const province = new THREE.Object3D();
          // shape平面
          // const shape = new THREE.Shape()
          let coordinates = []
          // type 为 Polygon时
          if(elem.geometry.type === 'Polygon') {
            coordinates = [elem.geometry.coordinates]
          } else if(elem.geometry.type === 'MultiPolygon') {
            // type 为MulitPolygon时
            coordinates = elem.geometry.coordinates;
          }
          // 将省份的属性  加进来
          province.properties = elem.properties
          coordinates.forEach( multiPolygon => {
            multiPolygon.forEach( polygon => {
              const lineMaterial = new THREE.LineBasicMaterial( { color: 0XF19553 } ); //0x3BFA9E
              const positions = [];
              const linGeometry = new THREE.BufferGeometry();
              for (let i = 0; i < polygon.length; i ++) {
                var pos = this.lglt2xyz( polygon[i][0], polygon[i][1], radius );
                // // 画shape
                // if (i === 0) {
                //   shape.moveTo(pos.x, pos.y)
                // }
                // shape.lineTo(pos.x, pos.y)
                positions.push( pos.x, pos.y, pos.z );
              }
              
              linGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
              const line = new THREE.Line( linGeometry, lineMaterial );
              province.add( line );
              // 添加shape平面
              // const extrudeSettings = {
              //   depth: 10,
              //   bevelEnabled: false,
              // }

              // const geometry = new THREE.ExtrudeGeometry(
              //   shape,
              //   extrudeSettings
              // )
              // const material = new THREE.MeshBasicMaterial({
              //   color: '#2defff',
              //   transparent: true,
              //   opacity: 0.6,
              // })
              // const mesh = new THREE.Mesh(geometry, material)
              // province.add( mesh );
            } );
          } );
          map.add( province );
        } );
        group.add( map );
      },
      // 载入json 画地图边框
      initMapBorder( chinaBJson ) {
        const map = new THREE.Object3D()
        // 遍历省份构建模型
        chinaBJson.features.forEach( elem => {
          const province = new THREE.Object3D();
          let coordinates = []
          // type 为 Polygon时
          if(elem.geometry.type === 'LineString') {
            coordinates = [elem.geometry.coordinates]
          } else if(elem.geometry.type === 'MultiLineString') {
            // type 为MulitPolygon时
            coordinates = elem.geometry.coordinates;
          }
          coordinates.forEach( MultiLineString => {
            // const lineMaterial = new THREE.LineBasicMaterial( { color: 0x3BFA9E } ); //0x3BFA9E
            const piontGeometry = new THREE.BufferGeometry();
            const positions = [];
            
            MultiLineString.forEach( LineString => {
                var pos = this.lglt2xyz( LineString[0], LineString[1], radius );
                positions.push( pos.x, pos.y, pos.z );
            } );
            piontGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
            
            // 设置着色器
            const { length } = positions;
            const percents = new Float32Array(length);
            for (let i = 0; i < length; i += 1) {
              percents[i] = i / length;
            }
            piontGeometry.setAttribute('percent', new THREE.BufferAttribute(percents, 1)); 
            let lineMaterial = this.createFlowLineMat();
            const point = new THREE.Points( piontGeometry, lineMaterial );
            province.add(point)
          } );
          map.add(province)
        } );
        group.add(map)
      },
      //  引入射线追踪  用于鼠标点击事件
      setRaycaster() {
        this.raycaster = new THREE.Raycaster()
        this.mouse = new THREE.Vector2()
        // div tooltip
        this.tooltip = document.getElementById('tooltip')
        const onMouseMove = (event) => {
          // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
          this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1
          this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1

          // 更改div位置
          this.tooltip.style.left = event.clientX + 2 + 'px'
          this.tooltip.style.top = event.clientY + 2 + 'px'
        }
        window.addEventListener('mousemove', onMouseMove, false)
      },
      // 显示tooltip
      showTip() {
        // 显示省份的信息
        if (this.lastPick) {
          const properties = this.lastPick.object.parent.properties

          this.tooltip.textContent = properties.name

          this.tooltip.style.visibility = 'visible'
        } else {
          this.tooltip.style.visibility = 'hidden'
        }
      },
      // 创建自定义流光自定义着色器
      createFlowLineMat() {
        const vertexShader = `
          attribute float percent;
          uniform float time;
          uniform float number;
          uniform float speed;
          uniform float length;
          varying float opacity;
          uniform float size;

          void main()
          {
              float l = clamp(1.0-length, 0.0, 1.0);

              gl_PointSize = clamp(fract(percent*number + l - time*number*speed)-l, 0.0, 1.) * size * (1./length);

              opacity = gl_PointSize/size;
              gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
          }
          `
        const fragmentShader = `
          varying float opacity;
          uniform vec3 color;

          void main(){
              if (opacity <=0.2){
                  discard;
              }
              gl_FragColor = vec4(color, 1.0);
          }
          `
        let uniforms = {
                time: this.commonUniforms.time,
                number: this.commonUniforms.number,
                speed: this.commonUniforms.speed,
                length: this.commonUniforms.length,
                size: this.commonUniforms.size,
                color: {
                    // value: new THREE.Color(Math.random(), Math.random(), Math.random())
                    value: new THREE.Color(0xffd700)
                }
            };
        let material = new THREE.ShaderMaterial({
                uniforms: uniforms,
                vertexShader: vertexShader,
                fragmentShader: fragmentShader,
                transparent: true,
                blending: THREE.AdditiveBlending,
            });

        return material;
      },
      // 创建光柱
      createBar() {
        let plane = new THREE.PlaneGeometry(2.5,8)
        let material = new THREE.MeshPhongMaterial({
          //设置矩形网格模型的纹理贴图(光柱特效)
            map: globeTextureLoader.load('static/textures/lightray_yellow.jpg'),
            // 双面显示
            side: THREE.DoubleSide,
            // 开启透明效果，否则颜色贴图map的透明不起作用
            transparent: true,
            color: '#0f0',
            blending: THREE.AdditiveBlending
        });
        // 矩形网格1
        let mesh1 = new THREE.Mesh(plane, material);
        // 克隆网格模型mesh1，并旋转90度
        let mesh2 = mesh1.clone().rotateY(Math.PI/2)
        let groupMesh= new THREE.Group()
        groupMesh.add(mesh1,mesh2);
        // 确定位置
        let pos = this.lglt2xyz(87,43,radius+2)
        groupMesh.position.set(pos.x, pos.y, pos.z)
        let size = radius * 0.01;//矩形平面Mesh的尺寸
        groupMesh.scale.set( size, size, size );//设置mesh大小
        // 设置方向
         // mesh在球面上的法线方向(球心和球面坐标构成的方向向量)
        let coordVec3 = new THREE.Vector3( pos.x, pos.y, pos.z ).normalize();
        let meshNormal = new THREE.Vector3( 0, 1, 0 );
        // 四元数属性.quaternion表示mesh的角度状态
        //.setFromUnitVectors();计算两个向量之间构成的四元数值
        groupMesh.quaternion.setFromUnitVectors( meshNormal, coordVec3 );
        // groupMesh.rotation.x = Math.PI/2
        groupMesh.rotation.z = Math.PI
        group.add( groupMesh )
      }, 
      // 添加坐标轴helper
      addAxesHelper() {
        console.log('omg')
        const axesHelper = new THREE.AxesHelper( 5000 );
        this.scene.add( axesHelper );
      }

  },
  mounted() {
    this.Dom = document.querySelector("#container");
    width = this.Dom.clientWidth;
    height = this.Dom.clientHeight;

    this.initRenderer();
    this.initCamera();
    this.initScene();
    this.initLight();
    this.initControls();
    // 添加背景粒子
    this.setPSys();
    // 添加坐标轴helper
    this.addAxesHelper()
    // 添加地球
    this.initEarth();
    // 添加地图旁边的大气层光圈
    this.initEarthCircle();
    // 添加卫星平面 及两个卫星
    // this.initWeiXingAndPlane();
    // 地图上添加标记点
    this.addMarkDot()
    // 地图上添加光柱
    this.createBar()
    // 根据geojson创建地图
    this.initMap(chinaJson)
    // 根据geojsonBorder 创建地图边框
    this.initMapBorder(chinaBorder)
    // 引入射线，用于鼠标
    this.setRaycaster()
    this.animate();
    window.addEventListener("resize", this.onWindowResize, false);
  }
};
</script>
<style scoped>

#tooltip {
  position: absolute;
  z-index: 2;
  background: white;
  padding: 10px;
  border-radius: 2px;
  visibility: hidden;
}
</style>